package edu.maxflow.mapreduce.util;

import java.util.ArrayList;
import java.util.List;

import edu.maxflow.Edge;
import edu.maxflow.Path;

public class Accumulator {

	private List<Edge> acceptedEdges = new ArrayList<Edge>();

	private long minimumRemainingFlow;

	// decides whether given path is acceptable or not
	public boolean accept(Path path) {
		if (path.containsSameEdge()) {
			return false;
		}

		return accept(new ArrayList<Edge>(path.getEdgeList()), false);
	}

	// returns maximum acceptable flow for the given path
	// if there is no flow, then return 0
	public long acceptAndReturnFlow(Path path) {
		if (path.containsSameEdge()) {
			return 0;
		}

		boolean result = accept(new ArrayList<Edge>(path.getEdgeList()), true);

		if (result) {
			return minimumRemainingFlow;
		} else {
			return 0;
		}
	}

	// used to concatenate source excess path and target excess path
	public boolean accept(Path path1, Path path2) {
		List<Edge> edgeList = new ArrayList<Edge>(path1.getEdgeList());
		edgeList.addAll(new ArrayList<Edge>(path2.getEdgeList()));

		return accept(edgeList, false);
	}

	private boolean accept(List<Edge> edgeList, boolean isTarget) {
		if (edgeList.size() == 0) {
			return false;
		}

		List<Edge> newEdges = new ArrayList<Edge>();

		minimumRemainingFlow = Long.MAX_VALUE;

		for (Edge pathEdge : edgeList) {
			boolean alreadyExists = false;

			for (Edge acceptedEdge : acceptedEdges) {
				if (acceptedEdge.equals(pathEdge)) {
					alreadyExists = true;
					long remainingFlow = acceptedEdge.getCapacity() - acceptedEdge.getFlow();
					if (remainingFlow < minimumRemainingFlow) {
						minimumRemainingFlow = remainingFlow;
					}
				}
			}

			if (!alreadyExists) {
				long remainingFlow = pathEdge.getCapacity() - pathEdge.getFlow();
				if (remainingFlow < minimumRemainingFlow) {
					minimumRemainingFlow = remainingFlow;
				}
			}
		}

		if (minimumRemainingFlow <= 0) {
			return false;
		}

		for (Edge pathEdge : edgeList) {
			boolean alreadyExists = false;

			for (Edge acceptedEdge : acceptedEdges) {
				if (acceptedEdge.equals(pathEdge)) {
					alreadyExists = true;

					long currentFlow = minimumRemainingFlow + acceptedEdge.getFlow();

					if (currentFlow > acceptedEdge.getCapacity()) {
						return false;
					} else {
						if (isTarget) {
							acceptedEdge.setFlow(currentFlow);
						}
					}

					break;
				}
			}

			if (!alreadyExists) {
				if (isTarget) {
					pathEdge.setFlow(pathEdge.getFlow() + minimumRemainingFlow);
					acceptedEdges.add(pathEdge);
				}
			}
		}

		acceptedEdges.addAll(newEdges);

		return true;
	}

	public List<Edge> getAcceptedEdges() {
		return acceptedEdges;
	}

}
